import 'dart:io';
import 'dart:math';

import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:flutter_network/service_method.dart';
import 'package:flutter_network/service_url.dart';
import 'package:wallet_utils/log_util.dart';
import 'package:wallet_utils/text_util.dart';
import 'package:path_provider/path_provider.dart';
import 'package:qiniu_flutter_sdk/qiniu_flutter_sdk.dart';
import 'package:uuid/uuid.dart';

import '../dio_manager.dart';
import 'image_token_response_bean.dart';

/// @author: pengboboer
/// @createDate: 2022/1/7

class OssUtils {
  /// 单独上传一张图片
  /// 不传quality根据图片大小自动计算压缩比例
  /// 若返回null则上传失败
  static Future<String?> uploadImage(String? path, {int? quality}) async {
    if (TextUtil.isEmpty(path)) return null;
    if (path!.startsWith("http")) return path;
    ImageTokenBean? bean = await Repository.getToken();
    if (bean == null) return null;
    return _compressUploadImage(path, bean, quality: quality);
  }

  /// 同时上传多张图片
  /// 不传quality根据图片大小自动计算压缩比例
  /// 若返回null则上传失败
  static Future<List<String>?> uploadMultiImage(List<String>? paths,
      {int? quality}) async {
    if (paths?.isEmpty ?? true) return null;
    ImageTokenBean? bean = await Repository.getToken();
    if (bean == null) return null;
    List<Future<String?>> futureList = [];
    for (String path in paths!) {
      futureList.add(_compressUploadImage(path, bean, quality: quality));
    }
    return Future.wait(futureList).then((values) {
      // 其中有一张图片上传失败，那么就算失败，目前逻辑先这样
      List<String>? list = [];
      for (String? item in values) {
        if (item == null) return null;
        list.add(item);
      }
      return list;
    }).catchError((error) {
      return null;
    });
  }

  /// 压缩和上传图片
  static Future<String?> _compressUploadImage(
      String path, ImageTokenBean tokenBean,
      {int? quality}) async {
    if (path.startsWith("http")) return path;
    int qualityValue = quality ?? await Repository.calculateQuality(path);
    // quality为100时直接上传原图
    // 上传原图时，如果是heic格式，必须走一次压缩，让其压成jpg格式,
    // heic格式的图片不能直接改后缀名为jpg,必须压缩才能变成jpg格式
    // 注意：低版本ios手机开启实况拍的heic格式的照片当前框架无法压缩
    if (qualityValue == 100 &&
        ImageFormatExtension.getFormat(path) != ImageFormat.heic) {
      return Repository.upload(path, tokenBean);
    }
    String? compressPath = await Repository.compressImage(path, qualityValue);
    bool isCompressFileExist = await File(compressPath ?? "").exists();
    if (!isCompressFileExist) return null;
    LogUtils.d("压缩比例----$qualityValue%"
        "\n图片压缩后地址------${compressPath ?? ""}"
        "\n原文件大小------${await File(path).length() / 1024}KB"
        "\n压缩后大小------${await File(compressPath ?? "").length() / 1024}KB");
    return Repository.upload(compressPath, tokenBean);
  }
}

class Repository {
  /// 获取七牛token
  static Future<ImageTokenBean?> getToken() async {
    Response response = await HttpRequest().requestGet(UploadPath.imageToken);
    if (response.statusCode == 200) {
      return ImageTokenResponseBean.fromJson(response.data).data;
    }
    return null;
  }

  /// 上传文件
  static Future<String?> upload(
      String? path, ImageTokenBean imageTokenBean) async {
    if (!await isValidFile(path)) {
      return null;
    }

    ImageFormat imageFormat = ImageFormatExtension.getFormat(path!);
    if (imageFormat == ImageFormat.none) {
      return null;
    }

    String key = DateTime.now().millisecondsSinceEpoch.toString() +
        Uuid().v4().substring(0, 5) +
        Random().nextInt(10000).toString() +
        imageFormat.getSuffix();
    return Storage()
        .putFile(File(path), imageTokenBean.imgToken!,
            options: PutOptions(key: key))
        .then((value) {
      String url = imageTokenBean.domain! + (value.key ?? "");
      LogUtils.d("上传后的图片地址：$url");
      return url;
    });
  }

  /// 压缩图片
  static Future<String?> compressImage(String path, int quality) async {
    ImageFormat imageFormat = ImageFormatExtension.getFormat(path);
    if (imageFormat == ImageFormat.none) {
      return null;
    }
    // 如果是heic格式的按jpg压缩
    if (imageFormat == ImageFormat.heic) {
      imageFormat = ImageFormat.jpg;
    }
    Directory directory = await getTemporaryDirectory();
    String tempPath = directory.path +
        "/imageTemp" +
        DateTime.now().millisecondsSinceEpoch.toString() +
        Random().nextInt(10000).toString() +
        imageFormat.getSuffix();
    File? file = await FlutterImageCompress.compressAndGetFile(path, tempPath,
        quality: quality, format: imageFormat.getCompressFormat());
    return file?.path;
  }

  /// 计算压缩比例
  static Future<int> calculateQuality(String? path) async {
    if (!await isValidFile(path)) {
      return 100;
    }
    File imageFile = File(path!);
    int quality = 100;
    LogUtils.d("图片路径：$path");
    LogUtils.d("图片大小：${imageFile.lengthSync() / 1024}Kb");
    if (imageFile.lengthSync() > 4 * 1024 * 1024) {
      quality = 30;
    } else if (imageFile.lengthSync() > 2 * 1024 * 1024) {
      quality = 40;
    } else if (imageFile.lengthSync() > 1 * 1024 * 1024) {
      quality = 50;
    } else if (imageFile.lengthSync() > 0.5 * 1024 * 1024) {
      quality = 60;
    } else if (imageFile.lengthSync() > 0.25 * 1024 * 1024) {
      quality = 80;
    }
    return quality;
  }

  /// 是否为有效文件
  static Future<bool> isValidFile(String? path) async {
    return !TextUtil.isEmpty(path) && await File(path!).exists();
  }
}

enum ImageFormat { jpg, png, heic, webp, none }

extension ImageFormatExtension on ImageFormat {
  static List<String> jpgSuffix = ["jpg", "jpeg", "JPG", "JPEG"];
  static List<String> pngSuffix = ["png", "PNG"];
  static List<String> heicSuffix = ["heic", "Heic", "HEIC"];
  static List<String> webpSuffix = ["webp", "Webp", "WEBP"];

  static ImageFormat getFormat(String path) {
    if (_parseType(path, jpgSuffix)) return ImageFormat.jpg;
    if (_parseType(path, pngSuffix)) return ImageFormat.png;
    if (_parseType(path, heicSuffix)) return ImageFormat.heic;
    if (_parseType(path, webpSuffix)) return ImageFormat.webp;
    return ImageFormat.none;
  }

  String getSuffix() {
    switch (this) {
      case ImageFormat.jpg:
        return ".jpg";
      case ImageFormat.png:
        return ".png";
      case ImageFormat.heic:
        return ".heic";
      case ImageFormat.webp:
        return ".webp";
      default:
        return "";
    }
  }

  CompressFormat getCompressFormat() {
    switch (this) {
      case ImageFormat.jpg:
        return CompressFormat.jpeg;
      case ImageFormat.png:
        return CompressFormat.png;
      case ImageFormat.heic:
        return CompressFormat.heic;
      case ImageFormat.webp:
        return CompressFormat.webp;
      default:
        return CompressFormat.jpeg;
    }
  }

  static bool _parseType(String path, List<String> suffix) {
    bool _result = false;
    for (int i = 0; i < suffix.length; i++) {
      if (path.endsWith(suffix[i])) {
        _result = true;
        break;
      }
    }
    return _result;
  }
}
